﻿using System;
using System.Globalization;
using System.Drawing;
using System.Reflection;
using System.Security.Permissions;
#if !PocketPC || DesignTime
using System.Runtime.Serialization;
#endif

namespace GeoFramework
{
	/// <summary>Represents a location on Earth of special significance.</summary>
	/// <remarks>
	/// 	<para>Waypoints are typically used to mark unique places such as positions along a
	///     route, destinations and starting points. This class provides features for labeling
	///     a location as well as giving it a color. This class is used by other products to
	///     create their own flavor of waypoints. For example, GIS.NET uses this class to mark
	///     special features on a map, whereas the Garmin® binary plug-in for GPS.NET uses this
	///     class to upload and download waypoints from Garmin devices.</para>
	/// 	<para>Instances of this class are not inherently thread-safe because properties can
	///     be modified after construction.</para>
	/// </remarks>
#if !PocketPC || DesignTime
#if Framework20
    [Obfuscation(Feature = "renaming", Exclude = false, ApplyToMembers = true)]
    [Obfuscation(Feature = "controlflow", Exclude = true, ApplyToMembers = true)]
    [Obfuscation(Feature = "stringencryption", Exclude = false, ApplyToMembers = true)]
#endif
    [Serializable()]
#endif
	public class Waypoint : Position, IDisposable
	{
		#region Private Variables

		private bool pVisible = true;
		private Color pForeColor;
        private Font pFont;
		private string pText;
        private ContentAlignment pTextAlign = ContentAlignment.TopCenter;

		#endregion

		#region  Constructors

		public Waypoint(Longitude longitude, Latitude latitude) 
			: base(longitude, latitude)
		{   
		}

		/// <overloads>Creates a new instance.</overloads>
		public Waypoint(Latitude latitude, Longitude longitude) 
			: base(latitude, longitude)
		{
		}

		/// <summary>
		/// Creates a new instance using the specified latitude, longitude and
		/// Datum.
		/// </summary>
		/// <remarks>
		/// This constructor is commonly used when a new position must be created using an
		/// Datum other than the default of WGS1984.
		/// </remarks>
		/// <param name="latitude">An angle from the equator between -90° and +90°.</param>
		/// <param name="longitude">An angle from the Prime Meridian between -90° and +90°.</param>
		/// <param name="datum">An Datum to associate with the given latitude and 
		/// longitude.</param>
		public Waypoint(Latitude latitude, Longitude longitude, Datum datum) 
			: base(latitude, longitude, datum)
		{
		}

		/// <summary>
		/// Creates a new instance by parsing latitude and longitude from a single string.
		/// </summary>
		/// <param name="value">A <strong>String</strong> containing both a latitude and longitude to parse.</param>
		public Waypoint(string value) 
			: base(value, CultureInfo.CurrentCulture)
		{}

        /// <summary>
        /// Creates a new instance by parsing latitude and longitude from a single string.
        /// </summary>
        /// <param name="value">A <strong>String</strong> containing both a latitude and longitude to parse.</param>
        public Waypoint(string value, CultureInfo culture)
            : base(value, culture)
        { }

//		/// <summary>
//		/// Creates a new instance as a Universal Transverse Mercator (UTM) coordinate
//		/// </summary>
//		/// <param name="zone">A <strong>string</strong> containing the UTM zone and letter.</param>
//		/// <param name="easting">A <strong>Distance</strong> measuring the distance East of the zone boundary.</param>
//		/// <param name="northing">A <strong>Distance</strong> measuring the distance North of the zone boundary.</param>
//		//[CLSCompliant(false)]
//		public Waypoint(string zone, Distance easting, Distance northing) 
//			: base(zone, easting, northing)
//		{
//		}

		//[CLSCompliant(false)]
		public Waypoint(ZoneLetter zoneLetter, int zoneNumber, Distance easting, Distance northing) 
			: base(zoneLetter, zoneNumber, easting, northing)
		{
		}

		//[CLSCompliant(false)]
		public Waypoint(ZoneLetter zoneLetter, int zoneNumber, Distance easting, Distance northing, Datum datum) 
			: base(zoneLetter, zoneNumber, northing, easting, datum)
		{
		}

        #if !PocketPC || DesignTime
        /// <summary>
        /// Creates a new instance by de-serializing previously-serialized information.
        /// </summary>
        /// <param name="info">A <strong>SerializationInfo</strong> object containing serialized data.</param>
        /// <param name="context">A <strong>StreamingContext</strong> object describing the context of serialization.</param>
        /// <remarks>This constructor will de-serialize a previously-serialized object.</remarks>
        protected Waypoint(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            pVisible = info.GetBoolean("Visible");
            pForeColor = (Color)info.GetValue("ForeColor", typeof(Color));
            pFont = (Font)info.GetValue("Font", typeof(Font));
            pText = info.GetString("Text");
            pTextAlign = (ContentAlignment)info.GetValue("TextAlign", typeof(ContentAlignment));
        }

        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
        protected override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            // Set the altitude
            info.AddValue("Visible", pVisible);
            info.AddValue("ForeColor", pForeColor);
            info.AddValue("Font", pFont);
            info.AddValue("Text", pText);
            info.AddValue("TextAlign", pTextAlign);

            // And any other items
            base.GetObjectData(info, context);
        }
#endif

		#endregion

        #region Events

        public event WaypointEventHandler TextAlignmentChanged;
        public event WaypointEventHandler TextChanged;
        public event WaypointEventHandler FontChanged;
        public event WaypointEventHandler ForeColorChanged;
        public event WaypointEventHandler VisibleChanged;

        #endregion

        #region Virtual Methods

        protected virtual void OnTextChanged(WaypointEventArgs e)
        {
            if(TextChanged != null)
#if PocketPC && !Framework20
                TextChanged(this, e);
#else
                DynamicEvent.BeginInvoke(TextChanged, this, e);
#endif
        }

        protected virtual void OnTextAlignmentChanged(WaypointEventArgs e)
        {
            if (TextAlignmentChanged != null)
#if PocketPC && !Framework20
                TextAlignmentChanged(this, e);
#else
                DynamicEvent.BeginInvoke(TextAlignmentChanged, this, e);
#endif
        }

        protected virtual void OnFontChanged(WaypointEventArgs e)
        {
            if (FontChanged != null)
#if PocketPC && !Framework20
                FontChanged(this, e);
#else
                DynamicEvent.BeginInvoke(FontChanged, this, e);
#endif
        }
        
        protected virtual void OnForeColorChanged(WaypointEventArgs e)
        {
            if (ForeColorChanged != null)
#if PocketPC && !Framework20
                ForeColorChanged(this, e);
#else
                DynamicEvent.BeginInvoke(ForeColorChanged, this, e);
#endif
        }

        protected virtual void OnVisibleChanged(WaypointEventArgs e)
        {
            if (VisibleChanged != null)
#if PocketPC && !Framework20
                VisibleChanged(this, e);
#else
                DynamicEvent.BeginInvoke(VisibleChanged, this, e);
#endif
        }
        #endregion

        #region Public Properties

        /// <summary>Controls whether or not the waypoint should be visible.</summary>
		public bool Visible
		{
			get
			{
				return pVisible;
			}
			set
			{
				if (pVisible == value) 
					return;
				pVisible = value;

                OnVisibleChanged(new WaypointEventArgs(this));
			}
		}

		/// <summary>Controls the color of the waypoint.</summary>
		[CLSCompliant(false)]
		public Color ForeColor
		{
			get
			{
				return pForeColor;
			}
			set
			{
                if (pForeColor.Equals(value))
                    return;

				pForeColor = value;

                OnForeColorChanged(new WaypointEventArgs(this));
			}
		}

		/// <summary>
		/// Controls the font used to render the text of the waypoint
		/// </summary>
		[CLSCompliant(false)]
		public Font Font
		{
			get
			{
				return pFont;
			}
			set
			{
				if(pFont.Equals(value))
					return;

				pFont = value;

				OnFontChanged(new WaypointEventArgs(this));
			}
		}

		/// <summary>Controls the label associated with the waypoint.</summary>
		public string Text
		{
			get
			{
				return pText == null ? "" : pText;
			}
			set
			{
				if (pText != null && pText.Equals(value)) 
                    return;
				pText = value;

                OnTextChanged(new WaypointEventArgs(this));

			}
		}

		/// <summary>
		/// Controls the position of text relative to the position of the waypoint.
		/// </summary>
		[CLSCompliant(false)]
        public ContentAlignment TextAlign
        {
            get
            {
                return pTextAlign;
            }
            set
            {
                if (pTextAlign.Equals(value))
                    return;
                pTextAlign = value;

                OnTextAlignmentChanged(new WaypointEventArgs(this));
            }
        }

        #endregion

        #region Public Members

        ~Waypoint()
        {
            Dispose(false);
        }

        public virtual void Dispose(bool unmanagedResourcesOnly)
        {
            if (unmanagedResourcesOnly)
            {
                if (pFont != null)
                    pFont.Dispose();
            }

            GC.SuppressFinalize(this);
        }

        public virtual void Dispose()
        {
            Dispose(true);
        }

        public void Show()
        {
            if (!pVisible)
                Visible = true;
        }

        public void Hide()
        {
            if (!pVisible)
                Visible = false;
        }

        #endregion

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            Dispose();
        }

        #endregion
    }

    public sealed class WaypointEventArgs : EventArgs
    {
        private Waypoint pWaypoint;

        public WaypointEventArgs(Waypoint waypoint)
        {
            pWaypoint = waypoint;
        }

        public Waypoint Waypoint
        {
            get
            {
                return pWaypoint;
            }
        }
    }

    
}

